home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / System / XADmaster / xad_dev / Sources / tools / exe2arc.c next >
Encoding:
C/C++ Source or Header  |  2001-04-01  |  15.5 KB  |  609 lines

  1. #define NAME         "exe2arc"
  2. #define DISTRIBUTION "(Freeware) "
  3. #define REVISION     "3"
  4. #define DATE         "07.07.2000"
  5.  
  6. /* Programmheader
  7.  
  8.     Name:        exe2arc
  9.     Author:        SDI
  10.     Distribution:    Freeware
  11.     Description:    strips executable header from exe files
  12.     Compileropts:    -
  13.     Linkeropts:    -gsi -l amiga
  14.  
  15.  1.0   02.11.99 : first version
  16.  1.1   06.11.99 : now also supports zip exes with wrong offsets
  17.  1.2   29.05.00 : renamed from exe2zip, added Ace, Rar, Cab, Arj, LhA
  18.  1.3   07.07.00 : added TYPE keyword to specify archive type
  19. */
  20.  
  21. #include <proto/exec.h>
  22. #include <proto/dos.h>
  23. #include <exec/memory.h>
  24. #include "SDI_version.h"
  25. #define SDI_TO_ANSI
  26. #include "SDI_ASM_STD_protos.h"
  27.  
  28. struct DosLibrary *     DOSBase = 0;
  29. struct ExecBase *     SysBase  = 0;
  30.  
  31. #define PARAM    "FROM/A,TO,TYPE/K"
  32.  
  33. struct Args {
  34.   STRPTR   from;
  35.   STRPTR   to;
  36.   STRPTR   type;
  37. };
  38.  
  39. #define BUFSIZE 102400
  40.  
  41. #define EndConvI32(a)    (((a)>>24)|(((a)>>8)&0xFF00)|(((a)<<8)&0xFF0000)|((a)<<24))
  42. #define EndConvI16(a)    ((UWORD)(((a)>>8)|((a)<<8)))
  43.  
  44. typedef BOOL (* SCANFUNC)(BPTR infh, STRPTR buffer, ULONG filesize, ULONG buffersize);
  45. typedef ULONG (* EXTRACTFUNC)(BPTR infh, BPTR outfh, STRPTR buffer, ULONG filesize, ULONG buffersize);
  46.  
  47. struct ScanData {
  48.   SCANFUNC    ScanFunc;
  49.   EXTRACTFUNC    ExtractFunc;
  50.   STRPTR    Extension; /* and type specifier */
  51.   STRPTR    Name; /* and type specifier */
  52. };
  53.  
  54. /* a) It is not the very best method to do scan loop again and again, but easy
  55.       to implement :-)
  56.    b) The buffer can be used to pass data from scanner to extractor.
  57. */
  58.  
  59. struct ScanData ScanFuncs[]; /* declaration, real field see file end */
  60.  
  61. ULONG start(void)
  62. {
  63.   ULONG ret = RETURN_FAIL;
  64.   struct DosLibrary *dosbase;
  65.  
  66.   SysBase = (*((struct ExecBase **) 4));
  67.   { /* test for WB and reply startup-message */
  68.     struct Process *task;
  69.     if(!(task = (struct Process *) FindTask(0))->pr_CLI)
  70.     {
  71.       WaitPort(&task->pr_MsgPort);
  72.       Forbid();
  73.       ReplyMsg(GetMsg(&task->pr_MsgPort));
  74.       return RETURN_FAIL;
  75.     }
  76.   }
  77.  
  78.   if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  79.   {
  80.     struct Args args;
  81.     struct RDArgs *rda;
  82.  
  83.     DOSBase = dosbase;
  84.  
  85.     args.to = args.type = 0;
  86.     if((rda = ReadArgs(PARAM, (LONG *) &args, 0)))
  87.     {
  88.       BPTR infh;
  89.  
  90.       if((infh = Open(args.from, MODE_OLDFILE)))
  91.       {
  92.         struct FileInfoBlock *fib;
  93.  
  94.         if((fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  95.         {
  96.           if(ExamineFH(infh, fib))
  97.           {
  98.             STRPTR buf;
  99.  
  100.             if((buf = AllocMem(BUFSIZE+512, MEMF_ANY)))
  101.             {
  102.           ULONG i, j;
  103.           LONG stop = 0, found = 0;
  104.  
  105.           for(i = 0; !stop && ScanFuncs[i].ScanFunc; ++i)
  106.           {
  107.         if(args.type && stricmp(ScanFuncs[i].Extension, args.type) && stricmp(ScanFuncs[i].Name, args.type))
  108.           ; /* skip the entry */
  109.                 else if(Seek(infh, 0, OFFSET_BEGINNING) >= 0)
  110.                 {
  111.           if(args.type)
  112.             found = 1;
  113.                   Printf("Scanning for %s-Archive.", ScanFuncs[i].Name);
  114.                   Flush(Output());
  115.                   if(ScanFuncs[i].ScanFunc(infh, buf, fib->fib_Size, BUFSIZE))
  116.                   {
  117.                     BPTR outfh;
  118.                     if(!args.to)
  119.                     {
  120.                       STRPTR tmp = buf+BUFSIZE;
  121.                       ULONG k;
  122.  
  123.                       args.to = tmp;
  124.                       for(j = 0; args.from[j]; ++j)
  125.                         tmp[j] = args.from[j];
  126.               k = j;
  127.  
  128.               do
  129.               {
  130.                 if(tmp[--j] == '.')
  131.                   k = j;
  132.               } while(j && tmp[j] != '.' && tmp[j] != '/' && tmp[j] != ':');
  133.               tmp[k++] = '.';
  134.               for(j = 0; ScanFuncs[i].Extension[j]; ++j)
  135.                 tmp[k++] = ScanFuncs[i].Extension[j];
  136.               tmp[k] = 0;
  137.                     }
  138.                     stop = 1;
  139.             Printf(" Found.\n");
  140.                     if((outfh = Open(args.to, MODE_NEWFILE)))
  141.                     {
  142.                       if((j = ScanFuncs[i].ExtractFunc(infh, outfh, buf, fib->fib_Size, BUFSIZE)))
  143.                       {
  144.                         Printf("Saved %ld byte to %s\nBe careful and test the file for correctness.\n", j, args.to);
  145.                         ret = 0;
  146.                       }
  147.                       Close(outfh);
  148.                       if(ret)
  149.                         DeleteFile(args.to);
  150.                     }
  151.                     else
  152.                       Printf("\nFailed to open output file.\n");
  153.                   }
  154.                   else
  155.                   {
  156.                     Printf("\r\033[K");
  157.                     Flush(Output());
  158.                   }
  159.                 }
  160.                 else
  161.                 {
  162.                   stop = 1;
  163.                   Printf("Failed to seek to file start.\n");
  164.                 }
  165.               }
  166.               if(!stop)
  167.               {
  168.                 if(args.type)
  169.                 {
  170.                   Printf("\r\033[KDid not find archive of type '%s'.\n", args.type);
  171.                   if(found)
  172.                     Printf("Maybe it is an archive of another type?\n");
  173.                   Printf("Valid types are: ");
  174.                   for(i = 0; !stop && ScanFuncs[i].ScanFunc; ++i)
  175.                     Printf("%s (%s)%s", ScanFuncs[i].Extension, ScanFuncs[i].Name, ScanFuncs[i+1].ScanFunc ? ", " : "\n");
  176.                 }
  177.                 else
  178.                   Printf("\r\033[KDid not find archive data.\n");
  179.               }
  180.               FreeMem(buf, BUFSIZE+512);
  181.             }
  182.             else
  183.               Printf("Failed to open temporary buffer.\n");
  184.           }
  185.           else
  186.             Printf("Failed to examine file.\n");
  187.           FreeDosObject(DOS_FIB, 0);
  188.         }
  189.         else
  190.           Printf("Failed to open file information object.\n");
  191.         Close(infh);
  192.       }
  193.       else
  194.         Printf("Failed to open input file.\n");
  195.       FreeArgs(rda);
  196.     }
  197.     else
  198.       PrintFault(IoErr(), 0);
  199.     CloseLibrary((struct Library *) dosbase);
  200.   } /* OpenLibrary dos */
  201.   return ret;
  202. }
  203.  
  204. ULONG DoCopy(BPTR infh, BPTR outfh, STRPTR buf, ULONG size, ULONG buffersize)
  205. {
  206.   ULONG s, err = 0;
  207.   
  208.   while(size && !err)
  209.   {
  210.     s = size;
  211.     if(s > buffersize)
  212.       s = buffersize;
  213.     if(Read(infh, buf, s) != s)
  214.       err = RETURN_FAIL;
  215.     else if(Write(outfh, buf, s) != s)
  216.       err = RETURN_FAIL;
  217.     size -= s;
  218.   }
  219.   
  220.   return err;
  221. }
  222.  
  223. /******** exe2zip *********/
  224.  
  225. BOOL ScanZIP(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  226. {
  227.   ULONG start = 0, corr = 0;
  228.   LONG stop = 0, i, j, k;
  229.  
  230.   if(Seek(fh, 0, OFFSET_END) >= 0)
  231.   {
  232.     while(size > 22 && !stop)
  233.     {
  234.       if(bufsize > size)
  235.         bufsize = size;
  236.       if(Seek(fh, size-bufsize, OFFSET_BEGINNING) < 0)
  237.         ++stop;
  238.       else if(Read(fh, buf, bufsize) != bufsize)
  239.         ++stop;
  240.       for(i = bufsize-22; i >= 0 && !stop; --i)
  241.       {
  242.         if(buf[i] == 'P' && buf[i+1] == 'K' && buf[i+2] == 5 && buf[i+3] == 6)
  243.         {
  244.           j = (((((buf[i+19]<<8)+buf[i+18])<<8)+buf[i+17])<<8)+buf[i+16];
  245.           k = (((((buf[i+15]<<8)+buf[i+14])<<8)+buf[i+13])<<8)+buf[i+12];
  246.           if(j != size-bufsize+i-k)
  247.       {
  248.         corr = (size-bufsize+i-k)-j;
  249.         j += corr;
  250.       }
  251.           if(Seek(fh, j+4, OFFSET_BEGINNING) >= 0)
  252.           {
  253.             if(Read(fh, buf, 42) == 42)
  254.               start = ((buf[38]) | (buf[39]<<8) | (buf[40]<<16) | (buf[41]<<24)) + corr;
  255.           }
  256.           ++stop;
  257.         }
  258.       }
  259.       size -= (bufsize-21);
  260.     }
  261.   }
  262.  
  263.   if(start)
  264.     Seek(fh, start, OFFSET_BEGINNING);
  265.   ((ULONG *)buf)[0] = corr; /* store this for extract */
  266.   ((ULONG *)buf)[1] = start;
  267.  
  268.   return (BOOL) (start ? TRUE : FALSE);
  269. }
  270.  
  271. ULONG ExtractZIP(BPTR infh, BPTR outfh, STRPTR buf, ULONG filesize, ULONG buffersize)
  272. {
  273.   LONG start, corr, Type = 0, ret = 0, i;
  274.  
  275.   start = ((ULONG *)buf)[1];
  276.   corr = ((ULONG *)buf)[0]-start;
  277.  
  278.   while(Type != 0x504B0506 && !ret && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
  279.   {
  280.     if(Read(infh, &Type, 4) == 4)
  281.     {
  282.       ret = RETURN_FAIL;
  283.       switch(Type)
  284.       {
  285.       case 0x504B0304: /* local */
  286.         if(Read(infh, buf+4, 26) == 26)
  287.         {
  288.           buf[0] = 'P'; buf[1] = 'K'; buf[2] = 3; buf[3] = 4;
  289.           if(Write(outfh, buf, 30) == 30)
  290.           {
  291.             ret = DoCopy(infh, outfh, buf, ((buf[18]) | (buf[19]<<8) | (buf[20]<<16) | (buf[21]<<24)) +
  292.             ((buf[26]) | (buf[27]<<8)) + ((buf[28]) | (buf[29]<<8)), buffersize);
  293.           }
  294.         }
  295.         break;
  296.       case 0x504B0102: /* central */
  297.         if(Read(infh, buf+4, 42) == 42)
  298.         {
  299.           buf[0] = 'P'; buf[1] = 'K'; buf[2] = 1; buf[3] = 2;
  300.           i = ((buf[42]) | (buf[43]<<8) | (buf[44]<<16) | (buf[45]<<24)) + corr;
  301.       buf[42] = i;
  302.       buf[43] = i>>8;
  303.       buf[44] = i>>16;
  304.       buf[45] = i>>24;
  305.           if(Write(outfh, buf, 46) == 46)
  306.           {
  307.             ret = DoCopy(infh, outfh, buf, ((buf[28]) | (buf[29]<<8)) +
  308.             ((buf[30]) | (buf[31]<<8)) + ((buf[32]) | (buf[33]<<8)), buffersize);
  309.           }
  310.         }
  311.         break;
  312.       case 0x504B0506: /* end */
  313.         if(Read(infh, buf+4, 18) == 18)
  314.         {
  315.           buf[0] = 'P'; buf[1] = 'K'; buf[2] = 5; buf[3] = 6;
  316.           i = ((buf[16]) | (buf[17]<<8) | (buf[18]<<16) | (buf[19]<<24)) + corr;
  317.       buf[16] = i;
  318.       buf[17] = i>>8;
  319.       buf[18] = i>>16;
  320.       buf[19] = i>>24;
  321.           if(Write(outfh, buf, 22) == 22)
  322.             ret = DoCopy(infh, outfh, buf, buf[20] | (buf[21]<<8), buffersize); /* copy comment */
  323.         }
  324.         break;
  325.       default:
  326.     Printf("Unknown or illegal data found.\n");
  327.     break;
  328.       }
  329.     }
  330.     else
  331.       Printf("Unexpected end of data.\n");
  332.   }
  333.   if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
  334.     SetIoErr(ERROR_BREAK);
  335.  
  336.   return ret ? 0 : filesize-start;
  337. }
  338.  
  339. /******** exe2ace *********/
  340.  
  341. BOOL ScanACE(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  342. {
  343.   ULONG start = 0;
  344.   LONG i, pos = 0, stop = 0;
  345.  
  346.   while(size > 14 && !stop)
  347.   {
  348.     if(bufsize > size)
  349.       bufsize = size;
  350.     if(Seek(fh, pos, OFFSET_BEGINNING) < 0)
  351.       ++stop;
  352.     else if(Read(fh, buf, bufsize) != bufsize)
  353.       ++stop;
  354.     for(i = 0; i <= bufsize-14 && !stop; ++i)
  355.     {
  356.       if(buf[i+7] == '*' && buf[i+8] == '*' && buf[i+9] == 'A' && buf[i+10] == 'C' &&
  357.       buf[i+11] == 'E' && buf[i+12] == '*' && buf[i+13] == '*')
  358.       {
  359.         if(Seek(fh, -i, OFFSET_CURRENT) >= 0)
  360.           start = pos+i;
  361.         ++stop;
  362.       }
  363.     }
  364.     size -= i;
  365.     pos += i;
  366.   }
  367.  
  368.   if(start)
  369.     Seek(fh, start, OFFSET_BEGINNING);
  370.   ((ULONG *)buf)[0] = start;
  371.  
  372.   return (BOOL) (start ? TRUE : FALSE);
  373. }
  374.  
  375. ULONG ExtractACE(BPTR infh, BPTR outfh, STRPTR buf, ULONG filesize, ULONG buffersize)
  376. {
  377.   LONG ret;
  378.  
  379.   filesize -= ((ULONG *)buf)[0];
  380.   ret = DoCopy(infh, outfh, buf, filesize, buffersize);
  381.   
  382.   return ret ? 0 : filesize;
  383. }
  384.  
  385. /******** exe2rar *********/
  386.  
  387. BOOL ScanRAR(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  388. {
  389.   ULONG start = 0;
  390.   LONG i, pos = 0, stop = 0;
  391.  
  392.   while(size > 7 && !stop)
  393.   {
  394.     if(bufsize > size)
  395.       bufsize = size;
  396.     if(Seek(fh, pos, OFFSET_BEGINNING) < 0)
  397.       ++stop;
  398.     else if(Read(fh, buf, bufsize) != bufsize)
  399.       ++stop;
  400.     for(i = 0; i <= bufsize-7 && !stop; ++i)
  401.     {
  402.       if(buf[i] == 'R' && buf[i+1] == 'a' && buf[i+2] == 'r' && buf[i+3] == '!' &&
  403.       buf[i+4] == 0x1A && buf[i+5] == 7 && buf[i+6] == 0)
  404.       {
  405.         if(Seek(fh, -i, OFFSET_CURRENT) >= 0)
  406.           start = pos+i;
  407.         ++stop;
  408.       }
  409.     }
  410.     size -= i;
  411.     pos += i;
  412.   }
  413.  
  414.   if(start)
  415.     Seek(fh, start, OFFSET_BEGINNING);
  416.   ((ULONG *)buf)[0] = start;
  417.  
  418.   return (BOOL) (start ? TRUE : FALSE);
  419. }
  420.  
  421. /******** exe2cab *********/
  422.  
  423. BOOL ScanCAB(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  424. {
  425.   ULONG start = 0, len = 0, foff;
  426.   LONG i, pos = 0, stop = 0;
  427.  
  428.   while(size > 20 && !stop)
  429.   {
  430.     if(bufsize > size)
  431.       bufsize = size;
  432.     if(Seek(fh, pos, OFFSET_BEGINNING) < 0)
  433.       ++stop;
  434.     else if(Read(fh, buf, bufsize) != bufsize)
  435.       ++stop;
  436.     for(i = 0; i <= bufsize-20 && !stop; ++i)
  437.     {
  438.       if(buf[i] == 'M' && buf[i+1] == 'S' && buf[i+2] == 'C' && buf[i+3] == 'F')
  439.       {
  440.         len = (buf[i+8]) | (buf[i+9]<<8) | (buf[i+10]<<16) | (buf[i+11]<<24);
  441.         foff = (buf[i+16]) | (buf[i+17]<<8) | (buf[i+18]<<16) | (buf[i+19]<<24);
  442.         if(len <= size-i && foff < len)
  443.         {
  444.           if(Seek(fh, -i, OFFSET_CURRENT) >= 0)
  445.             start = pos+i;
  446.           ++stop;
  447.         }
  448.       }
  449.     }
  450.     size -= i;
  451.     pos += i;
  452.   }
  453.  
  454.   if(start)
  455.     Seek(fh, start, OFFSET_BEGINNING);
  456.   ((ULONG *)buf)[0] = len;
  457.  
  458.   return (BOOL) (start ? TRUE : FALSE);
  459. }
  460.  
  461. ULONG ExtractCAB(BPTR infh, BPTR outfh, STRPTR buf, ULONG filesize, ULONG buffersize)
  462. {
  463.   LONG ret;
  464.  
  465.   ret = DoCopy(infh, outfh, buf, (filesize = ((ULONG *)buf)[0]), buffersize);
  466.   
  467.   return ret ? 0 : filesize;
  468. }
  469.  
  470. /******** exe2arj *********/
  471.  
  472. BOOL ScanARJ(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  473. {
  474.   ULONG start = 0, len, cbuf[256], j, k, l;
  475.   LONG i, pos = 0, stop = 0;
  476.   STRPTR mem;
  477.  
  478.   while(size > 50 && !stop)
  479.   {
  480.     if(bufsize > size)
  481.       bufsize = size;
  482.     if(Seek(fh, pos, OFFSET_BEGINNING) < 0)
  483.       ++stop;
  484.     else if(Read(fh, buf, bufsize) != bufsize)
  485.       ++stop;
  486.     for(i = 0; i <= bufsize-50 && !stop; ++i)
  487.     {
  488.       if(buf[i] == 0x60 && buf[i+1] == 0xEA)
  489.       {
  490.         len = (buf[i+2]) | (buf[i+3]<<8);
  491.         if(size-i > len+4)
  492.         {
  493.           if(bufsize-i < len+4)
  494.             break;
  495.           else
  496.           {
  497.             for(l = 0; l < 256; ++l)
  498.             {
  499.               k = l;
  500.  
  501.               for(j = 0; j < 8; ++j)
  502.               {
  503.                 if(k & 1)
  504.                   k = (k >> 1) ^ 0xEDB88320;
  505.                 else
  506.                   k >>= 1;
  507.               }
  508.               cbuf[l] = k;
  509.             }
  510.             l = ~0;
  511.             k = len;
  512.             mem = buf+i+4;
  513.  
  514.             while(k--)
  515.               l = cbuf[(l ^ *mem++) & 0xFF] ^ (l >> 8);
  516.             if(~l == ((mem[0]) | (mem[1]<<8) | (mem[2]<<16) | (mem[3]<<24)))
  517.             {
  518.               if(Seek(fh, -i, OFFSET_CURRENT) >= 0)
  519.                 start = pos+i;
  520.               ++stop;
  521.             }
  522.           }
  523.         }
  524.       }
  525.     }
  526.     size -= i;
  527.     pos += i;
  528.   }
  529.  
  530.   if(start)
  531.     Seek(fh, start, OFFSET_BEGINNING);
  532.   ((ULONG *)buf)[0] = start;
  533.  
  534.   return (BOOL) (start ? TRUE : FALSE);
  535. }
  536.  
  537. /******** exe2lha *********/
  538.  
  539. BOOL ScanLHA(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  540. {
  541.   ULONG start = 0;
  542.  
  543.   if(Read(fh, buf, 100) < 0)
  544.     return 0;
  545.   if(!buf[0] && !buf[1] && buf[2] == 3 && buf[3] == 0xF3 && buf[44] == 'S'
  546.   && buf[45] == 'F' && buf[46] == 'X' && buf[47] == '!')
  547.   {
  548.     start = (buf[55]) | (buf[54]<<8) | (buf[53]<<16) | (buf[52]<<24);
  549.     if(Seek(fh, start, OFFSET_BEGINNING) < 0)
  550.       start = 0;
  551.   }
  552.  
  553.   if(start)
  554.     Seek(fh, start, OFFSET_BEGINNING);
  555.   ((ULONG *)buf)[0] = start;
  556.  
  557.   return (BOOL) (start ? TRUE : FALSE);
  558. }
  559.  
  560. /******** exe2lzh *********/
  561.  
  562. BOOL ScanLZH(BPTR fh, STRPTR buf, ULONG size, ULONG bufsize)
  563. {
  564.   ULONG start = 0;
  565.   LONG i, pos = 0, stop = 0;
  566.  
  567.   while(size > 21 && !stop)
  568.   {
  569.     if(bufsize > size)
  570.       bufsize = size;
  571.     if(Seek(fh, pos, OFFSET_BEGINNING) < 0)
  572.       ++stop;
  573.     else if(Read(fh, buf, bufsize) != bufsize)
  574.       ++stop;
  575.     for(i = 0; i <= bufsize-21 && !stop; ++i)
  576.     {
  577.       if(buf[i+2] == '-' && buf[i+3] == 'l' && (buf[i+4] == 'h' || buf[i+4] == 'z') &&
  578.       buf[i+6] == '-' && buf[20] <= 2)
  579.       {
  580.         if(Seek(fh, -i, OFFSET_CURRENT) >= 0)
  581.           start = pos+i;
  582.         ++stop;
  583.       }
  584.     }
  585.     size -= i;
  586.     pos += i;
  587.   }
  588.  
  589.   if(start)
  590.     Seek(fh, start, OFFSET_BEGINNING);
  591.   ((ULONG *)buf)[0] = start;
  592.  
  593.   return (BOOL) (start ? TRUE : FALSE);
  594. }
  595.  
  596. /**************************/
  597.  
  598. struct ScanData ScanFuncs[] = {
  599. {ScanZIP, ExtractZIP, "zip", "Zip"},
  600. {ScanACE, ExtractACE, "ace", "Ace"},
  601. {ScanRAR, ExtractACE, "rar", "Rar"}, /* reuse ExtractACE */
  602. {ScanCAB, ExtractCAB, "cab", "Cabinet"},
  603. {ScanARJ, ExtractACE, "arj", "Arj"}, /* reuse ExtractACE */
  604. {ScanLHA, ExtractACE, "lha", "LhA"}, /* reuse ExtractACE */
  605. {ScanLZH, ExtractACE, "lzh", "Amiga-LhA"}, /* reuse ExtractACE */
  606. {0,0},
  607. };
  608.  
  609.